#
# picorine.py
#
#
# picorine: a framework for developing simple generators
#
# Copyright (C) 2004, 2005 Raphael Marvie 
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
# 02111-1307, USA.
# http://www.fsf.org/licensing/licenses/gpl.html
#
# Author contact: raphael.marvie@lifl.fr
#

'''
Module for writing simple generators based upon simple python based
templates.

This module also provides a simple generic factory to load a XML file
into a repository generated from its DTD.
'''

__version__ = '0.3.0'
__date__    = 'Thu Dec 16 22:23:40 CET 2004'
__author__  = 'Raphael Marvie <raphael.marvie@lifl.fr>'

from xml.dom import minidom

class generator:
    '''
    Simple base generator implementation. This class manages the
    writing of templates providing a dict or a tuple as parameters, as
    well as the output files for generation.

    When writing a generator using this base class, you have to
    provide a module containing the template definitions as follows:
    
    - it contains single class tmpl containing templates defined
      using Python format strings to be used with a dict, this class
      contains attributes set to the format string;
      
    - it contains single class tstr containing templates defined using
      Python format strings to be used with a tuple, this class
      contains attributes set to the format string.

      class tstr:
          simple_import = "import %s \n"

      class tmpl:
          complex_import = "from %(package)s import %(module)s \n"

    The templates names are intended to be used through write_tmpl()
    and write_tstr() methods provided by this class. examples:

      self.write_tmpl ("complex_import", {"package": "xml.dom", "module": "minidom"})

      self.write_tstr ("simple_import", ("time",))
    '''

    def __init__ (self, model, tmodule, outdir=None, ext='py'):
        '''constructor :o)
        model: model to be used for generation (tree of python objects) 
        tmodule: module containing the templates (object)
        outdir: output directory (default is current)
        ext: extension for generated files (default py)
        '''
        self.model = model
        self.tmodule = tmodule
        if outdir:
            self.outdir = outdir
        else:
            self.outdir = '.'
        self.ext = ext
        self.output = None

    def open (self, name, append=False):
        '''Opens a new file for class generation
        name: name of the file
        append: if true, the file is open for append (not re-created)
        '''
        self.close ()
        if append:
            mode = 'a'
        else:
            mode = 'w'
        self.output = open ('%s/%s.%s' % (self.outdir, name, self.ext), mode)

    def close (self):
        '''Close file currently used for generation (if one is already opened)'''
        if self.output:
            self.output.close ()
            self.output = None

    def write_str (self, sname, values=tuple()):
        tsrt = getattr (self.tmodule.tstr, sname)
        self.output.write (tsrt % values)

    def write_tmpl (self, tname, values=dict()):
        tmpl = getattr (self.tmodule.tmpl, tname)
        self.output.write (tmpl % values)
    
    def get_tmpl (self, tname, values=dict()):
        tmpl = getattr (self.tmodule.tmpl, tname)
        return (tmpl % values)
    
    def write_file(self, file_content):
        self.output.write (file_content)

class factory:
    '''Generic factory to create python object version of xml files
    '''

    def __init__ (self, impl):
        '''impl: module objet of the python objets to be used
        (generated by dtd2rep.py)'''
        self.impl = impl

    def _load_attr (self, elt, obj):
        '''Inner method for loading an attribute
        elt: xml node
        obj: obj to be added attribute values'''
        for attr in elt.attributes.values ():
            name = attr.nodeName
            value = attr.nodeValue
            setattr (obj, name, value)

    def _load_elem (self, elt):
        '''Inner method for loading an element
        elt: elt to be loaded as a python object
        '''
        name = elt.nodeName
        clss = getattr (self.impl, name)
        obj = clss ()
        self._load_attr (elt, obj)
        for c in elt.childNodes:
            # only considering element nodes
            if c.nodeType == c.ELEMENT_NODE:
                cobj = self._load_elem (c)
                cname = c.nodeName
                # get the proper function for adding the child
                if hasattr (obj, 'set_' + cname):
                    op = getattr (obj, 'set_' + cname)
                elif hasattr (obj, 'add_' + cname):
                    op = getattr (obj, 'add_' + cname)
                op (cobj)
        return obj
        
    def create (self, xmlfile):
        '''Create a python object representation of the xmlfile given
        as parameter '''
        doc = minidom.parse (xmlfile) 
        return self._load_elem (doc.documentElement)
    

